package edu.tjut.tjutcommunity.handler;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dtflys.forest.http.ForestHeader;
import com.dtflys.forest.http.ForestResponse;
import edu.tjut.tjutcommunity.TjutCommunityApplication;
import edu.tjut.tjutcommunity.entity.LoginBody;
import edu.tjut.tjutcommunity.entity.UserCookie;
import edu.tjut.tjutcommunity.exception.CookieExpiredException;
import edu.tjut.tjutcommunity.exception.UsernameOrPasswordErrorExecption;
import edu.tjut.tjutcommunity.httpInterface.LoginHttp;
import okhttp3.*;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Objects;

@Service
public class LoginHandler {

    private final static Logger logger = LoggerFactory.getLogger(TjutCommunityApplication.class);
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    ResourceLoader resourceLoader;

    /**
     * 处理登录请求，返回从学校服务器获得的和登录有关信息
     */
    //forest框架发出的post请求失败了，不知原因
    private static final OkHttpClient client = new OkHttpClient().newBuilder()
            .followRedirects(false)  //禁止OkHttp的重定向操作，我们自己处理重定向
            .build();
    static ScriptEngineManager manager;

    static ScriptEngine engine;

    final
    LoginHttp loginHttp;

    public LoginHandler(LoginHttp loginHttp) throws IOException {
        this.loginHttp = loginHttp;
    }

    //    static FileReader reader;
    static String reader;

    static {
        manager = new ScriptEngineManager();
        engine = manager.getEngineByName("javascript");
        try {
            ClassPathResource resource = new ClassPathResource("static/key.js");
            InputStream inputStream = resource.getInputStream();
            int ch;
            StringBuilder sb = new StringBuilder();
            while ((ch = inputStream.read()) != -1)
                sb.append((char) ch);
            reader = sb.toString();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取加密后的密码
     *
     * @param password 原始密码
     * @param slat     盐
     * @return
     * @throws Exception
     */
    private String getPassword(String password, String slat) throws Exception {
        engine.eval(reader);
        if (engine instanceof Invocable) {
            Invocable invoke = (Invocable) engine;    // 调用merge方法，并传入两个参数
            return (String) invoke.invokeFunction("encryptAES", password, slat);
        }
        return "";
    }

    public String getUserDepartment(UserCookie userCookie, String stuId) throws CookieExpiredException {
        if (userCookie == null) userCookie = getCookie(stuId);
        if (userCookie == null) throw new CookieExpiredException();
        String cookie = userCookie.getCOOKIE_ROUTE() + "; " +
                userCookie.getCOOKIE_JSESSIONID() + "; " +
                userCookie.getCOOKIE_IPLANETDIRECTORYPRO() + "; " +
                userCookie.getCOOKIE_MOD_AUTH_CAS() + "; " +
                userCookie.getCOOKIE_ASESSIONID();
        ForestResponse<String> userInfo = loginHttp.getUserInfo(cookie);
        JSONObject jsonObject = JSONObject.parseObject(userInfo.getContent());
        //获取到学院
        return (String) jsonObject.get("userDepartment");
    }

    public UserCookie getCookie(String stuId) {
        String cookie_Json = stringRedisTemplate.opsForValue().get("cookie_" + stuId);
        if (cookie_Json == null) return null;
        return JSON.parseObject(cookie_Json, UserCookie.class);
    }

    /**
     * 从学校的接口登录。
     *
     * @param stuId
     * @param password
     * @return
     * @throws Exception
     */
    public UserCookie login(String stuId, String password, UserCookie userCookie) throws Exception {
        //该cookie后续要添加到redis中
        LoginBody loginBody = new LoginBody();
        //获取index页面里的String COOKIE_ROUTE, COOKIE_JSESSIONID;
        String salt = step1(userCookie, loginBody);
        //获取密码
        password = getPassword(password, salt).replace("+", "%2B");
        if (password.isEmpty()) return null;
        loginBody.setUsername(stuId);
        loginBody.setPassword(password);
        MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
        //学号
        String URL = step1_2(stuId, password, userCookie, loginBody, mediaType);
        if (URL == null) throw new UsernameOrPasswordErrorExecption();
        //第二个重定向 需要Cookie:route+JSESSIONID+iPlanetDirectoryPro
        URL = step2(userCookie, URL);
        assert URL != null;
        step3(userCookie, URL);
        return userCookie;
    }

    public static void setSSFWCookie(UserCookie userCookie) throws IOException {
        String URL;
        step4(userCookie);
        URL = step5(userCookie);
        step6(userCookie, URL);
    }

    //最后两次验证
    private static void step6(UserCookie userCookie, String URL) throws IOException {
        Response response = null;
        Request request;
        String cookie8 = userCookie.getCOOKIE_JSESSIONID_SSFW() + "; " +
                userCookie.getCOOKIE_IPLANETDIRECTORYPRO();
        request = new Request.Builder()
                .url(URL)
                .addHeader("Cookie", cookie8)
                .build();
        try {
            response = client.newCall(request).execute();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (response != null) response.close();
        }

        URL = response.header("Location");
        if (URL == null) {
            return;
        }
        request = new Request.Builder()
                .url(URL)
                .addHeader("Cookie", cookie8)
                .build();
        try {
            response = client.newCall(request).execute();
            URL = response.header("Location");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            response.close();
        }
        request = new Request.Builder()
                .url(URL)
                .addHeader("Cookie", cookie8)
                .build();
        try {
            response = client.newCall(request).execute();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            response.close();
        }
    }

    //发起验证请求，发送了两次验证
    private static String step5(UserCookie userCookie) throws IOException {
        String URL;
        Response response = null;
        Request request;

        String cookie6 = userCookie.getCOOKIE_JSESSIONID_SSFW() + "; " +
                userCookie.getCOOKIE_IPLANETDIRECTORYPRO();
        URL = "http://ssfw.tjut.edu.cn/ssfw/cas_index.jsp";
        request = new Request.Builder()
                .url(URL)
                .addHeader("Cookie", cookie6)
                .build();
        try {
            response = client.newCall(request).execute();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (response != null) response.close();
        }

        URL = response.header("Location");
        String cookie7 = userCookie.getCOOKIE_CASTGC() + "; " +
                userCookie.getCOOKIE_ROUTE() + "; " +
                userCookie.getCOOKIE_JSESSIONID() + "; " +
                userCookie.getCOOKIE_IPLANETDIRECTORYPRO();
        request = new Request.Builder()
                .url(URL)
                .addHeader("Cookie", cookie7)
                .build();
        try {
            response = client.newCall(request).execute();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            response.close();
        }
        URL = response.header("Location");
        return URL;
    }

    /**
     * 师生服务的cookie获取，这里获取到ssfw的sessionid，但是还需要验证
     *
     * @param userCookie
     * @throws IOException
     */
    private static void step4(UserCookie userCookie) throws IOException {
        String URL;
        Response response = null;
        Request request;
        String cookie4 = userCookie.getCOOKIE_CASTGC() + "; " +
                userCookie.getCOOKIE_ROUTE() + "; " +
                userCookie.getCOOKIE_JSESSIONID() + "; " +
                userCookie.getCOOKIE_IPLANETDIRECTORYPRO();
        URL = "http://authserver.tjut.edu.cn/authserver/login?service=http%3A%2F%2Fssfw.tjut.edu.cn%2Fssfw%2Fcas_index.jsp";
        request = new Request.Builder()
                .url(URL)
                .addHeader("Cookie", cookie4)
                .build();
        try {
            response = client.newCall(request).execute();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (response != null) response.close();
        }

        assert response != null;
        URL = response.header("Location");
        String cookie5 = userCookie.getCOOKIE_IPLANETDIRECTORYPRO();
        assert URL != null;
        request = new Request.Builder()
                .url(URL)
                .addHeader("Cookie", cookie5)
                .build();
        try {
            response = client.newCall(request).execute();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            response.close();
        }
        //COOKIE_JSESSIONID_SSFW
        userCookie.setCOOKIE_JSESSIONID_SSFW(Objects.requireNonNull(response.header("Set-Cookie")).split(";", 2)[0]);
    }

    /**
     * 第二次重定向地址，设置asessionid，到这里index的cookie已全部获取到
     *
     * @param userCookie
     * @param URL
     * @throws IOException
     */
    private void step3(UserCookie userCookie, String URL) throws IOException {
        Response response = null;
        Request request;
        List<String> headers;
        //第三个重定向 Cookie需要 route+jsessionId+iPlanetDirectoryPro+MOD_AUTH_CAS
        String cookie3 = userCookie.getCOOKIE_ROUTE() + "; " +
                userCookie.getCOOKIE_JSESSIONID() + "; " +
                userCookie.getCOOKIE_IPLANETDIRECTORYPRO() + "; " +
                userCookie.getCOOKIE_MOD_AUTH_CAS();
        request = new Request.Builder()
                .url(URL)
                .addHeader("Cookie", cookie3)
                .build();
        try {
            response = client.newCall(request).execute();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (response != null) response.close();
        }
        headers = response.headers("set-cookie");
        //返回一个asessionid
        headers.forEach(header -> {
            if (header.startsWith("asessionid")) {
                userCookie.setCOOKIE_ASESSIONID(header.split(";", 2)[0]);
            } else if (header.startsWith("route")) {
                userCookie.setCOOKIE_ROUTE(header.split(";", 2)[0]);
            }
        });
    }

    /**
     * 设置cookie MOD_AUTH_CAS
     *
     * @param userCookie
     * @param URL
     * @return
     * @throws IOException
     */
    private String step2(UserCookie userCookie, String URL) throws IOException {
        Request request;
        Response response = null;
        String cookie2 = userCookie.getCOOKIE_IPLANETDIRECTORYPRO() + "; " +
                userCookie.getCOOKIE_ROUTE() + "; " +
                userCookie.getCOOKIE_JSESSIONID();

        request = new Request.Builder()
                .url(URL)
                .addHeader("Cookie", cookie2)
                .addHeader("Referer", "http://authserver.tjut.edu.cn/")
                .build();
        try {
            response = client.newCall(request).execute();
            URL = response.header("Location");
            //MOD_AUTH_CAS
            userCookie.setCOOKIE_MOD_AUTH_CAS(Objects.requireNonNull(response.header("Set-Cookie")).split(";", 2)[0]);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (response != null) response.close();
        }
        return URL;
    }

    /**
     * 第一次发起login请求，设置CASTGC，CASPRIVACY，IPLANETDIRECTORYPRO
     *
     * @param stuId
     * @param password
     * @param userCookie
     * @param loginBody
     * @param mediaType
     * @return 返回第一个重定向地址
     * @throws IOException
     */
    private String step1_2(String stuId, String password, UserCookie userCookie, LoginBody loginBody, MediaType mediaType) throws IOException {
        //获取到之后，请求登录
        String url = "http://authserver.tjut.edu.cn/authserver/login;jsessionid=" + userCookie.getCOOKIE_JSESSIONID().split("=")[1] + "?service=http%3A%2F%2Fssfw.tjut.edu.cn%2Fssfw%2Fcas_index.jsp";
        String cookie1 = userCookie.getCOOKIE_ROUTE() + "; " + userCookie.getCOOKIE_JSESSIONID();
        RequestBody Body = RequestBody.create(mediaType, "username=" + stuId + "&password=" + password + "&lt=" + loginBody.getLt() + "&dllt=" + loginBody.getDllt() + "&execution=" + loginBody.getExecution() + "&_eventId=" + loginBody.get_eventId() + "&rmShown=" + loginBody.getRmShown());
        //查询登录接口
        Request request = new Request.Builder()
                .url(url)
                .post(Body)
                .addHeader("Cookie", cookie1)
                .build();
        String URL = null;
        Response response = null;
        try {
            response = client.newCall(request).execute();
            List<String> headers = response.headers("set-cookie");
            headers.forEach(header -> {
                if (header.startsWith("CASTGC")) {
                    //CASTGC
                    userCookie.setCOOKIE_CASTGC(header.split(";", 2)[0]);
                } else if (header.startsWith("CASPRIVACY")) {
                    //CASPRIVACY
                    userCookie.setCOOKIE_CASPRIVACY(header.split(";", 2)[0]);
                } else if (header.startsWith("iPlanetDirectoryPro")) {
                    //iPlanetDirectoryPro
                    userCookie.setCOOKIE_IPLANETDIRECTORYPRO(header.split(";", 2)[0]);
                }
            });


            URL = response.header("Location");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (response != null) response.close();
        }
        return URL;
    }

    /**
     * 请求index，获取ROUTE和JSESSIONID
     *
     * @param userCookie
     * @param loginBody
     * @return 盐
     */
    private String step1(UserCookie userCookie, LoginBody loginBody) {
        ForestResponse<String> index = loginHttp.index();
        //分析页面获取参数
        Document doc = Jsoup.parse(index.getContent());
        String salt = doc.selectXpath("//*[@id=\"pwdDefaultEncryptSalt\"]").attr("value");
        String lt = doc.selectXpath("//*[@id=\"casLoginForm\"]/input[1]").attr("value");
        String dllt = doc.selectXpath("//*[@id=\"casLoginForm\"]/input[2]").attr("value");
        String execution = doc.selectXpath("//*[@id=\"casLoginForm\"]/input[3]").attr("value");
        String _eventId = doc.selectXpath("//*[@id=\"casLoginForm\"]/input[4]").attr("value");
        String rmShown = doc.selectXpath("//*[@id=\"casLoginForm\"]/input[5]").attr("value");
        loginBody.setLt(lt);
        loginBody.setDllt(dllt);
        loginBody.setExecution(execution);
        loginBody.set_eventId(_eventId);
        loginBody.setRmShown(rmShown);

        List<ForestHeader> cookies = index.getHeaders("Set-Cookie");
        //route
        userCookie.setCOOKIE_ROUTE(cookies.get(0).getValue().split(";")[0]);
        //jsessionid
        userCookie.setCOOKIE_JSESSIONID(cookies.get(1).getValue().split(";")[0]);

        return salt;
    }
}
